/**
 * @param {string} code
 * @return {boolean}
 */
export var isValid = function (code) {
  const n = code.length
  const tags = []
  let i = 0
  while (i < n) {
    if (code[i] === '<') {
      // '<' 结尾时必定为false
      if (i === n - 1) {
        return false
      }
      if (code[i + 1] === '/') {
        // ** 如果下一个字符是'/'，检查是否结束标签

        // 查找有没有 > 没有的话，则返回false
        const j = code.indexOf('>', i)
        if (j < 0) {
          return false
        }
        // 查出标签名, 在 '</' 和 '>' 之间
        const tagname = code.slice(i + 2, j)
        // 如果没有标签名，或在栈中不存在，则返回false
        if (tags.length === 0 || tags[tags.length - 1] !== tagname) {
          return false
        }
        // 如果标签名匹配，则弹出栈顶元素
        tags.pop()
        i = j + 1
        // 如果栈中没有标签名了，但code还未结束，则返回false
        if (tags.length === 0 && i !== n) {
          return false
        }
      } else if (code[i + 1] === '!') {
        // ** 检查是否 <![CDATA 标签

        if (tags.length === 0) {
          return false
        }
        // <![CDATA[ 至少占9个字符，超出code长度则返回false
        if (i + 9 > n) {
          return false
        }
        const cdata = code.slice(i + 2, i + 9)
        if ('[CDATA[' !== cdata) {
          return false
        }
        // 查找 CDATA 结束标签
        const j = code.indexOf(']]>', i)
        if (j < 0) {
          return false
        }
        i = j + 1
      } else {
        // ** 如果不是以上两种情况，则检查是否一个开始标签
        // 开始标签必须有 >, <和> 之前便是tagname
        const j = code.indexOf('>', i)
        if (j < 0) {
          return false
        }
        const tagname = code.slice(i + 1, j)
        if (tagname.length < 1 || tagname.length > 9) {
          return false
        }
        // 检查标签名是否合法(大写A和Z之间)
        for (let k = 0; k < tagname.length; ++k) {
          if (!(tagname[k] >= 'A' && tagname[k] <= 'Z')) {
            return false
          }
        }
        // 将标签名压入栈中
        tags.push(tagname)
        i = j + 1
      }
    } else {
      if (tags.length === 0) {
        return false
      }
      ++i
    }
  }
  return tags.length === 0
}

/**
 * 正则
 * @param {string} code
 * @return {boolean}
 */
export var isValid2 = function (code) {
  while (/<!\[CDATA\[.*?\]\]>/.test(code)) {
    code = code.replace(/<!\[CDATA\[.*?\]\]>/g, '$TMP$')
  }

  while (/<([A-Z]{1,9})>([^<]*)<\/(\1)>/.test(code)) {
    code = code.replace(/<([A-Z]{1,9})>([^<]*)<\/(\1)>/g, '$VALID$')
  }

  return code === '$VALID$'
}
